﻿/// Heron language interpreter for Windows in C#
/// http://www.heron-language.com
/// Copyright (c) 2009 Christopher Diggins
/// Licenced under the MIT License 1.0 
/// http://www.opensource.org/licenses/mit-license.php

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Xml;
using Peg;

namespace HeronEngine
{
    /// <summary>
    /// The HeronParser class creates typed representations of a Heron syntax tree.
    /// It is constructed from the concrete syntax tree generated by the PEG parser.
    /// Essentially this contains the objects that represent the classes, 
    /// functions, statements, and expressions that make up a Heron program. 
    /// One thing I do that is non-standard is convert expressions from a linear 
    /// form generated by the parser into their correct precedences. 
    /// My rationale is that parsers should not know about operator precedence.
    /// </summary>
    public class CodeModelBuilder : HeronValue
    {
        public class CodeModelException : Exception
        {
            public CodeModelException(string s)
                : base(s)
            {
            }

            public CodeModelException(Exception e)
                : base(e.Message, e)
            {
            }

            public CodeModelException(string s, Exception e)
                : base(s, e)
            {
            }
        }

        #region utility functions
        static private void Assure(ParseNode x, bool b, string s)
        {
            if (!b)
                throw new ParsingException(x.msText, x.mnBegin, x.mnBegin + x.mnCount, null, s);
        }
        #endregion

        #region construct parsing functions
        private static void CreateModuleDefn(ModuleDefn m, ParseNode x)
        {
            ParseNode imports = x.GetChild("imports");
            if (imports != null)
            {
                foreach (ParseNode import in imports.Children)
                {
                    string modAlias = import.GetChild(0).ToString();
                    string modDefName = import.GetChild(1).ToString().RemoveInternalWSpace();
                    ExpressionList args = CreateCompoundExpr(import.GetChild(2));
                    m.AddImport(modAlias, modDefName, args);
                }
            }

            ParseNode inherits = x.GetChild("inherits");
            if (inherits != null)
            {
                if (inherits.GetNumChildren() != 1)
                    throw new Exception("A module can only inherit from exactly one other module");
                m.SetBaseClass(CreateTypeRef(inherits.GetChild(0)));
            }

            ParseNode methods = x.GetChild("methods");
            if (methods != null)
            {
                foreach (ParseNode node in methods.Children)
                {
                    FunctionDefn f = CreateMethod(node, m);
                    m.AddMethod(f);
                }
            }

            ProcessFields(x, m);
        }

        public static T Create<T>(string s, Rule r, Func<ParseNode, T> f)
        {
            ParseNode node = null;
            string output = "";
            bool result = Parser.Parse(r, s, out output, out node);
            if (!result)
                return default(T);
            if (node.GetNumChildren() == 0)
                throw new Exception("Failed to parse string '" + s + "' as " + r.Name);
            return f(node.GetChild(0));
        }

        public static ModuleDefn CreateModule(ProgramDefn pd, ParseNode x, string sFileName)
        {
            ParseNode nameNode = x.GetChild(0);
            ModuleDefn r = new ModuleDefn(pd, nameNode.ToString(), sFileName);

            ParseNode bodyNode = x.GetChild(1);
            CreateModuleDefn(r, bodyNode);

            // Now store all of the types
            for (int i=2; i < x.GetNumChildren(); ++i) {
                ParseNode child = x.GetChild(i);
                switch (child.Label)
                {
                    case "class":
                        CreateClass(r, child);
                        break;
                    case "interface":
                        CreateInterface(r, child);
                        break;
                    case "enum":
                        CreateEnum(r, child);
                        break;
                    default:
                        throw new Exception("Unrecognized module sub-element " + child.Label);
                }
            }

            return r;
        }

        public static string GetNameNode(ParseNode x)
        {
            ParseNode name = x.GetChild("name");
            if (name == null)
                throw new Exception("Could not find name node");
            return name.ToString();
        }

        [HeronVisible]
        public static ClassDefn CreateClass(ModuleDefn m, string s)
        {
            return Create<ClassDefn>(s, HeronGrammar.Class, (ParseNode node) => CreateClass(m, node));
        }

        public static ClassDefn CreateClass(ModuleDefn m, ParseNode x)
        {
            string name = x.GetChild("name").ToString();
            ClassDefn r = new ClassDefn(m, name);

            ParseNode inherits = x.GetChild("inherits");
            if (inherits != null)
            {
                if (inherits.GetNumChildren() != 1)
                    throw new Exception("A class can only inherit from exactly one other class");
                ParseNode type = inherits.GetChild(0);
                r.SetBaseClass(CreateTypeRef(type));
            }

            ParseNode implements = x.GetChild("implements");
            if (implements != null)
                foreach (ParseNode node in implements.Children)
                    r.AddImplementedInterface(CreateTypeRef(node));

            ParseNode methods = x.GetChild("methods");
            if (methods != null)
            {
                foreach (ParseNode node in methods.Children)
                {
                    FunctionDefn f = CreateMethod(node, r);
                    r.AddMethod(f);
                }
            }

            ProcessFields(x, r);

            m.AddClass(r);
            return r;
        }

        static void ProcessFields(ParseNode x, ClassDefn defn)
        {
            Dictionary<string, Expression> initializers = new Dictionary<string, Expression>();
            ParseNode fields = x.GetChild("fields");
            if (fields != null)
            {
                foreach (ParseNode node in fields.Children)
                {
                    FieldDefn fd = CreateField(node);
                    fd.annotations = CreateAnnotations(node);
                    defn.AddField(fd);

                    ParseNode exprNode = node.GetChild("expr");
                    if (exprNode != null)
                    {
                        Expression expr = CreateExpr(exprNode);
                        initializers.Add(fd.name, expr);
                    }
                }
            }

            foreach (string s in initializers.Keys)
            {
                Assignment ass = new Assignment(new Name(s), initializers[s]);
                ExpressionStatement st = new ExpressionStatement(ass);
                defn.GetAutoConstructor().body.statements.Add(st);
            }
        }

        [HeronVisible]
        public static InterfaceDefn CreateInterface(ModuleDefn m, string s)
        {
            return Create<InterfaceDefn>(s, HeronGrammar.Interface, (ParseNode node) => CreateInterface(m, node));
        }

        public static InterfaceDefn CreateInterface(ModuleDefn m, ParseNode x)
        {
            string name = x.GetChild("name").ToString();
            InterfaceDefn r = new InterfaceDefn(m, name);

            ParseNode inherits = x.GetChild("inherits");
            if (inherits != null)
                foreach (ParseNode node in inherits.Children)
                    r.AddBaseInterface(CreateTypeRef(node));

            ParseNode methods = x.GetChild("methods");
            if (methods != null)
            {
                foreach (ParseNode node in methods.Children)
                {
                    FunctionDefn f = CreateMethod(node, r);
                    r.AddMethod(f);
                }
            }

            m.AddInterface(r);
            return r;
        }

        [HeronVisible]
        public static EnumDefn CreateEnum(ModuleDefn m, string s)
        {
            return Create<EnumDefn>(s, HeronGrammar.Enum, (ParseNode node) => CreateEnum(m, node));
        }

        public static EnumDefn CreateEnum(ModuleDefn m, ParseNode x)
        {
            string name = x.GetChild("name").ToString();
            EnumDefn r = new EnumDefn(m, name);

            ParseNode values = x.GetChild("values");
            if (values != null)
            {
                foreach (ParseNode node in values.Children)
                {
                    r.AddValue(node.ToString());
                }
            }

            m.AddEnum(r);
            return r;
        }

        [HeronVisible]
        public static FieldDefn CreateField(string s)
        {
            s = s.Trim();
            if (s.Length == 0)
                return null;
            if (s[s.Length - 1] != ';')
                s = s + ";";
            return Create<FieldDefn>(s, HeronGrammar.Field, (ParseNode node) => CreateField(node));
        }

        public static FieldDefn CreateField(ParseNode x)
        {
            FieldDefn r = new FieldDefn();
            r.name = x.GetChild("name").ToString();
            r.type = CreateTypeRef(x.GetChild("typedecl")); 
            if (x.HasChild("expr"))
                r.expr = CreateExpr(x.GetChild("expr"));
            return r;
        }

        [HeronVisible]
        public static FormalArg CreateFormalArg(string s)
        {
            return Create<FormalArg>(s, HeronGrammar.Param, (ParseNode node) => CreateFormalArg(node));
        }

        public static FormalArg CreateFormalArg(ParseNode x)
        {
            string name = x.GetChild("name").ToString();
            return new FormalArg(name, CreateTypeRef(x.GetChild("typedecl")));
        }

        [HeronVisible]
        public static FormalArgs CreateFormalArgs(string s)
        {
            return Create<FormalArgs>(s, HeronGrammar.ArgList, (ParseNode node) => CreateFormalArgs(node));
        }

        public static FormalArgs CreateFormalArgs(ParseNode x)
        {
            FormalArgs r = new FormalArgs();
            foreach (ParseNode node in x.Children)
                r.Add(CreateFormalArg(node));
            return r;
        }

        [HeronVisible]
        public static FunctionDefn CreateMethod(string s, HeronType parent)
        {
            return Create<FunctionDefn>(s, HeronGrammar.Method, (ParseNode node) => CreateMethod(node, parent));
        }

        public static ExpressionList CreateAnnotations(ParseNode x)
        {
            ParseNode anns = x.GetChild("annotations");
            ExpressionList r = new ExpressionList();
            if (anns == null)
                return r;
            foreach (ParseNode y in anns.Children)
                r.Add(CreateExpr(y));
            return r;
        }

        public static FunctionDefn CreateMethod(ParseNode x, HeronType parent)
        {
            ModuleDefn module = parent.GetModule();
            FunctionDefn r = new FunctionDefn(parent);
            r.annotations = CreateAnnotations(x);
            r.node = x;
            ParseNode fundecl = x.GetChild("fundecl");
            r.name = fundecl.GetChild("name").ToString();
            r.formals = CreateFormalArgs(fundecl.GetChild("arglist"));
            ParseNode rt = x.GetChild("typedecl");
            r.rettype = CreateTypeRef(rt);
            ParseNode codeblock = x.GetChild("codeblock");
            r.body = CreateCodeBlock(codeblock);
            return r;
        }

        public static TypeRef CreateTypeRef(ParseNode x)
        {
            if (x == null)
                return new TypeRef();

            bool bNullable = false;

            if (x.Label == "typedecl")
            {
                bNullable = x.GetChild("nullable") != null;
                x = x.GetChild("typeexpr");
                if (x == null)
                    throw new Exception("Type declaration is missing type expression");
            }

            if (x.Label != "typeexpr")
                throw new Exception("Expected a type-expression or type-declaration");

            ParseNode name = x.GetChild(0);
            if (name.Label != "name")
                throw new Exception("Expected a name child node of a type-expression");

            string sName = name != null ? name.ToString() : "Void";

            List<TypeRef> args = new List<TypeRef>();
            for (int i = 1; i < x.GetNumChildren(); ++i)
            {
                ParseNode arg = x.GetChild(i);
                if (arg.Label != "typeexpr")
                    throw new Exception("Expected child nodes to be type-expressions");
                args.Add(CreateTypeRef(arg));
            }

            return new TypeRef(sName, bNullable, args);
        }
        #endregion 

        #region statement parsing functions.
        [HeronVisible]
        public static Statement CreateStatement(string s)
        {
            return Create<Statement>(s, HeronGrammar.Statement, (ParseNode node) => CreateStatement(node));
        }

        public static Statement CreateStatement(ParseNode x)
        {
            switch (x.Label)
            {
                case "codeblock":
                    return CreateCodeBlock(x);
                case "vardecl":
                    return CreateVarDecl(x);
                case "if":
                    return CreateIfStatement(x);
                case "switch":
                    return CreateSwitchStatement(x);
                case "foreach":
                    return CreateForEachStatement(x);
                case "for":
                    return CreateForStatement(x);
                case "while":
                    return CreateWhileStatement(x);
                case "return":
                    return CreateReturnStatement(x);
                case "exprstatement":
                    return CreateExprStatement(x);
                case "delete":
                    throw new Exception("unimplemented");
                default:
                    throw new Exception("Unrecognized statement node " + x.Label);
            }
        }

        public static CodeBlock CreateCodeBlock(ParseNode x)
        {
            if (x == null)
                return new CodeBlock(null);
            CodeBlock r = new CodeBlock(x);
            foreach (ParseNode node in x.Children)
                r.statements.Add(CreateStatement(node));
            return r;
        }

        public static VariableDeclaration CreateVarDecl(ParseNode x)
        {
            VariableDeclaration r = new VariableDeclaration(x);
            r.annotations = CreateAnnotations(x);
            string name = x.GetChild("name").ToString();
            ParseNode tmp = x.GetChild("expr");
            TypeRef tr = CreateTypeRef(x.GetChild("typedecl"));    
            r.vardesc = new VarDesc(name, tr);
            if (tmp != null)
                r.value = CreateExpr(tmp);
            return r;
        }

        public static IfStatement CreateIfStatement(ParseNode x)
        {
            IfStatement r = new IfStatement(x);
            r.condition = CreateExpr(x.GetChild(0).GetChild(0));
            r.ontrue = CreateStatement(x.GetChild(1));
            if (x.GetNumChildren() > 2)
                r.onfalse = CreateStatement(x.GetChild(2));
            return r;
        }

        public static CaseStatement CreateCaseStatement(ParseNode x)
        {
            CaseStatement r = new CaseStatement(x);
            r.condition = CreateExpr(x.GetChild(0).GetChild(0));
            r.statement = CreateCodeBlock(x.GetChild(1));
            return r;
        }

        public static CodeBlock CreateDefaultStatement(ParseNode x)
        {
            return CreateCodeBlock(x.GetChild(0));
        }

        public static SwitchStatement CreateSwitchStatement(ParseNode x)
        {
            SwitchStatement r = new SwitchStatement(x);
            r.condition = CreateExpr(x.GetChild(0).GetChild(0));
            ParseNode cases = x.GetChild(1);
            foreach (ParseNode y in cases.Children)
            {
                CaseStatement cs = CreateCaseStatement(y);
                r.cases.Add(cs);
            }
            if (x.GetNumChildren() > 2)
            {
                r.ondefault = CreateDefaultStatement(x.GetChild(2));
            }
            return r;
        }

        public static ReturnStatement CreateReturnStatement(ParseNode x)
        {
            ReturnStatement r = new ReturnStatement(x);
            if (x.GetNumChildren() > 0)
                r.expression = CreateExpr(x.GetChild(0));
            return r;
        }

        public static WhileStatement CreateWhileStatement(ParseNode x)
        {
            WhileStatement r = new WhileStatement(x);
            r.condition = CreateExpr(x.GetChild(0).GetChild(0));
            r.body = CreateStatement(x.GetChild(1));
            return r;
        }

        public static ExpressionStatement CreateExprStatement(ParseNode x)
        {
            ExpressionStatement r = new ExpressionStatement(x);
            r.expression = CreateExpr(x.GetChild(0));
            return r;
        }

        public static ForEachStatement CreateForEachStatement(ParseNode x)
        {
            ForEachStatement r = new ForEachStatement(x);
            
            if (x.GetNumChildren() == 3)
            {
                // This is a foreach without a type declaration
                r.name = x.GetChild(0).ToString();
                r.type = new TypeRef("Unknown");
                r.collection = CreateExpr(x.GetChild(1));
                r.body = CreateStatement(x.GetChild(2));
            }
            else if (x.GetNumChildren() == 4)
            {
                // This is a foreach with a type declaration
                r.name = x.GetChild(0).ToString();
                r.type = CreateTypeRef(x.GetChild(1));
                r.collection = CreateExpr(x.GetChild(2));
                r.body = CreateStatement(x.GetChild(3));
            }
            else
            {
                throw new Exception("Foreach statements should only have three or four nodes");
            }
            return r;
        }

        public static ForStatement CreateForStatement(ParseNode x)
        {
            ForStatement r = new ForStatement(x);
            r.name = x.GetChild(0).ToString();
            r.initial = CreateExpr(x.GetChild(1));
            r.condition = CreateExpr(x.GetChild(2));
            r.next = CreateExpr(x.GetChild(3));
            r.body = CreateStatement(x.GetChild(4));
            return r;
        }
        #endregion

        #region utility functions
        public static char ToSpecialChar(ParseNode x, char c)
        {
            switch (c)
            {
                case 't':
                    return '\t';
                case 'n':
                    return '\n';
                case '\\':
                    return '\\';
                case '\"':
                    return '\"';
                case '\'':
                    return '\'';
                default:
                    Assure(x, false, "illegal char escape sequence \\" + c);
                    // Unreachable code added to avoid compilation error
                    return '?';
            }
        }

        public static char CharFromLiteral(ParseNode x, string s)
        {
            Assure(x, s.Length == 3 || s.Length == 4, "invalid char literal");
            Assure(x, s[0] == '\'', "expected single quotation marks around char");
            Assure(x, s[s.Length - 1] == '\'', "expected single quotation marks around char");
            if (s.Length == 3)
                return s[1];
            Assure(x, s[1] == '\\', "invalid char literal");
            return ToSpecialChar(x, s[2]);

        }

        public static string StringFromLiteral(ParseNode x, string s)
        {
            Assure(x, s.Length >= 2, "invalid string");
            Assure(x, s[0] == '"', "Expected quotation marks around string");
            Assure(x, s[s.Length - 1] == '"', "Expected quotation marks around string");
            StringBuilder r = new StringBuilder();
            for (int i = 1; i < s.Length - 1; ++i)
            {
                if (s[i] == '\\')
                {
                    i++;
                    r.Append(ToSpecialChar(x, s[i]));
                }
                else
                {
                    r.Append(s[i]);
                }
            }
            return r.ToString();
        }

        public static string StringFromVerbatimLiteral(ParseNode x, string s)
        {
            Assure(x, s.Length >= 3, "invalid string");
            Assure(x, s[0] == '@', "Expected @ sign leading string");
            Assure(x, s[1] == '"', "Expected quotation marks around string");
            Assure(x, s[s.Length - 1] == '"', "Expected quotation marks around string");
            StringBuilder r = new StringBuilder();
            for (int i = 2; i < s.Length - 1; ++i)
            {
                if (s[i] == '"')
                {
                    i++;
                    r.Append("\"");
                    i++;
                }
                else
                {
                    r.Append(s[i]);
                }
            }
            return r.ToString();
        }
        #endregion

        /// The following functions create an expression tree from a list of 
        /// nodes representing expressions. When parsing, expressions are treated as a flat group
        /// These function use implicit precedence rules to construct the appropriate tree structure.
        /// Most of these functions will return an CompoundExpr object. They also take a single ParseNode 
        /// representing the list of parsed expression nodes, and a current index into the list called 
        /// "i". 
        #region Expression creating functions
        
        private static bool ChildNodeMatches(ParseNode x, ref int i, string s)
        {
            if (i >= x.GetNumChildren())
                return false;
            if (x.GetChild(i).ToString() == s)
            {
                i++;
                return true;
            }
            return false;
        }

        // TODO: clean-up all of the error checking and stuff. 
        // Make this stuff into functions.

        public static NewExpr CreateNewExpr(ParseNode x)
        {
            Assure(x, x.GetNumChildren() >= 2, "new operator must be followed by type expression and arguments in paranthesis");
            ParseNode type = x.GetChild(0);
            Assure(x, type.Label == "typeexpr", "new operator is missing type expression");
            ParseNode args = x.GetChild(1);
            Assure(args, args.Label == "paranexpr", "new operator is missing argument list");
            
            string module = null;
            if (x.GetNumChildren() > 2)
                module = x.GetChild(2).ToString();

            return new NewExpr(CreateTypeRef(type), CreateCompoundExpr(args), module);
        }

        public static MapExpr CreateMapExpr(ParseNode x)
        {
            Assure(x, x.GetNumChildren() == 3, "map operator must have three child nodes");
            ParseNode name = x.GetChild(0);
            ParseNode list = x.GetChild(1);
            ParseNode func = x.GetChild(2);
            return new MapExpr(name.ToString(), CreateExpr(list), CreateExpr(func));
        }

        public static SelectExpr CreateSelectExpr(ParseNode x)
        {
            Assure(x, x.GetNumChildren() == 3, "select operator must have three child nodes");
            ParseNode name = x.GetChild(0);
            ParseNode list = x.GetChild(1);
            ParseNode func = x.GetChild(2);
            return new SelectExpr(name.ToString(), CreateExpr(list), CreateExpr(func));
        }

        public static AccumulateExpr CreateAccumulateExpr(ParseNode x)
        {
            Assure(x, x.GetNumChildren() == 5, "accumulate operator must have five child nodes");
            ParseNode acc = x.GetChild(0);
            ParseNode init = x.GetChild(1);
            ParseNode each = x.GetChild(2);
            ParseNode list = x.GetChild(3);
            ParseNode expr = x.GetChild(4);
            return new AccumulateExpr(acc.ToString(), CreateExpr(init), each.ToString(), CreateExpr(list), CreateExpr(expr));
        }

        public static ReduceExpr CreateReduceExpr(ParseNode x)
        {
            Assure(x, x.GetNumChildren() == 4, "reduce operator must have four child nodes");
            string a = x.GetChild(0).ToString();
            string b = x.GetChild(1).ToString();
            Expression list = CreateExpr(x.GetChild(2));
            Expression expr = CreateExpr(x.GetChild(3));
            return new ReduceExpr(a, b, list, expr) ;
        }

        public static Expression CreateNullaryOperatorExpr(ParseNode x)
        {
            switch (x.ToString())
            {
                case "null":
                    return new NullExpr();
                case "true":
                    return new BoolLiteral(true);
                case "false":
                    return new BoolLiteral(false);
            }
            throw new Exception("Not a recognized keyword/operator " + x.ToString());
        }

        public static Expression CreatePrimaryExpr(ParseNode x, ref int i)
        {
            Assure(x, i < x.GetNumChildren(), "sub-expression index went out of bounds");
            ParseNode child = x.GetChild(i);

            string sLabel = child.Label;
            string sVal = child.ToString();
            Assure(x, sVal.Length > 0, "illegal zero-length child expression");
            switch (sLabel)
            {
                case "new":
                    i++;
                    return CreateNewExpr(child);
                case "specialname":
                    i++;
                    return CreateNullaryOperatorExpr(child);
                case "name":
                    i++;
                    return new Name(child.ToString());
                case "int":
                    i++;
                    return new IntLiteral(int.Parse(sVal));
                case "char":
                    i++;
                    return new CharLiteral(CharFromLiteral(child, sVal));
                case "string":
                    i++;
                    return new StringLiteral(StringFromLiteral(child, sVal));
                case "verbstring":
                    i++;
                    return new StringLiteral(StringFromVerbatimLiteral(child, sVal));
                case "float":
                    i++;
                    return new FloatLiteral(float.Parse(sVal));
                case "bin":
                    i++;
                    throw new Exception("binary literals not yet supported");
                case "hex":
                    i++;
                    throw new Exception("hexadecimal literals not yet supported");
                case "paranexpr":
                    Assure(child, child.GetNumChildren() == 1, "can only have one expression node in a paranthesized expression");
                    ParseNode tmp = child.GetChild(0);
                    i++;
                    return new ParanthesizedExpr(CreateExpr(tmp));
                case "bracketedexpr":
                    i++;
                    return new TupleExpr(CreateCompoundExpr(child));
                case "map":
                    i++;
                    return CreateMapExpr(child);
                case "select":
                    i++;
                    return CreateSelectExpr(child);
                case "accumulate":
                    i++;
                    return CreateAccumulateExpr(child);
                case "reduce":
                    i++;
                    return CreateReduceExpr(child);
                default:
                    Assure(child, false, "unrecognized primary expression: '" + sLabel + "'");
                    return null; // unreachable
            }
        }

        public static ExpressionList CreateCompoundExpr(ParseNode x)
        {
            ExpressionList r = new ExpressionList();
            foreach (ParseNode child in x.Children)
            {
                int i = 0;
                Expression tmp = CreateExpr(child, ref i);
                r.Add(tmp);
            }
            return r;
        }

        public static Expression CreatePostfixExpr(ParseNode x, ref int i)
        {
            int old = i;
            Expression r = CreatePrimaryExpr(x, ref i);
            Assure(x, r != null, "unable to create primary expression");

            while (i < x.GetNumChildren())
            {
                old = i;
                ParseNode tmp = x.GetChild(i);
                
                if (tmp.Label == "bracketedexpr")
                {
                    i++;
                    Assure(x, tmp.GetNumChildren() == 1, "brackets must contain only a single sub-expression");
                    r = new ReadAt(r, CreateExpr(tmp.GetChild(0)));
                }
                else if (tmp.Label == "paranexpr")
                {
                    i++;
                    r = new FunCall(r, CreateCompoundExpr(tmp));
                }
                else if (tmp.ToString() == ".")
                {
                    i++;
                    Assure(x, x.GetNumChildren() > i, "a '.' operator must be followed by a valid name expression");
                    tmp = x.GetChild(i);
                    Assure(x, tmp.Label == "name", "a '.' operator must be followed by a valid name expression");
                    string sName = tmp.ToString();
                    Assure(x, sName.Length > 0, "Name expression must have non-zero length");
                    i++;
                    r = new ChooseField(r, sName);
                }
                else if (tmp.ToString() == "++")
                {                    
                    // You can't have anything to the left of ++
                    i++;
                    return new PostIncExpr(r);
                }
                else
                {
                    return r;
                }
                Assure(x, i > 0, "internal error, failed to advance sub-expression index");
            }

            return r;
        }

        public static Expression CreateUnaryExpr(ParseNode x, ref int i)
        {
            if (ChildNodeMatches(x, ref i, "-"))
            {
                Expression tmp = CreatePostfixExpr(x, ref i);
                Expression r = new UnaryOperation("-", tmp);
                return r;
            }
            if (ChildNodeMatches(x, ref i, "!"))
            {
                Expression tmp = CreatePostfixExpr(x, ref i);
                Expression r = new UnaryOperation("!", tmp);
                return r;
            }
            else
            {
                Expression r = CreatePostfixExpr(x, ref i);
                return r;
            }
        }

        public static Expression CreateMultExpr(ParseNode x, ref int i)
        {
            Expression r = CreateUnaryExpr(x, ref i);

            if (ChildNodeMatches(x, ref i, "*"))
            {
                r = new BinaryOperation("*", r, CreateUnaryExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "/"))
            {
                r = new BinaryOperation("/", r, CreateUnaryExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "%"))
            {
                r = new BinaryOperation("%", r, CreateUnaryExpr(x, ref i));
            }
            return r;
        }

        public static Expression CreateAddExpr(ParseNode x, ref int i)
        {
            Expression r = CreateMultExpr(x, ref i);

            while (i < x.GetNumChildren())
            {
                if (ChildNodeMatches(x, ref i, "+"))
                {
                    r = new BinaryOperation("+", r, CreateMultExpr(x, ref i));
                }
                else if (ChildNodeMatches(x, ref i, "-"))
                {
                    r = new BinaryOperation("-", r, CreateMultExpr(x, ref i));
                }
                else
                {
                    return r;
                }
            }
            return r;
        }

        public static Expression CreateRelExpr(ParseNode x, ref int i)
        {
            Expression r = CreateAddExpr(x, ref i);

            if (ChildNodeMatches(x, ref i, ">"))
            {
                r = new BinaryOperation(">", r, CreateAddExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "<"))
            {
                r = new BinaryOperation("<", r, CreateAddExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, ">="))
            {
                r = new BinaryOperation(">=", r, CreateAddExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "<="))
            {
                r = new BinaryOperation("<=", r, CreateAddExpr(x, ref i));
            }
            return r;
        }

        public static Expression CreateTypeOpExpr(ParseNode x, ref int i)
        {
            Expression r = CreateRelExpr(x, ref i);

            if (ChildNodeMatches(x, ref i, "is"))
            {
                r = new BinaryOperation("is", r, CreateRelExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "as"))
            {
                r = new BinaryOperation("as", r, CreateRelExpr(x, ref i));
            }
            return r;

        }

        public static Expression CreateEqExpr(ParseNode x, ref int i)
        {
            Expression r = CreateTypeOpExpr(x, ref i);

            if (ChildNodeMatches(x, ref i, "=="))
            {
                r = new BinaryOperation("==", r, CreateTypeOpExpr(x, ref i));
            }
            else if (ChildNodeMatches(x, ref i, "!="))
            {
                r = new BinaryOperation("!=", r, CreateTypeOpExpr(x, ref i));
            }
            return r;
        }

        public static Expression CreateXOrExpr(ParseNode x, ref int i)
        {
            Expression r = CreateEqExpr(x, ref i);
            if (ChildNodeMatches(x, ref i, "^^")) 
                r = new BinaryOperation("^^", r, CreateEqExpr(x, ref i));
            return r;
        }

        public static Expression CreateAndExpr(ParseNode x, ref int i)
        {
            Expression r = CreateXOrExpr(x, ref i);
            if (ChildNodeMatches(x, ref i, "&&"))
                r = new BinaryOperation("&&", r, CreateXOrExpr(x, ref i));
            return r;
        }

        static Expression CreateOrExpr(ParseNode x, ref int i)
        {
            Expression r = CreateAndExpr(x, ref i);
            if (ChildNodeMatches(x, ref i, "||"))
                r = new BinaryOperation("||", r, CreateAndExpr(x, ref i));
            return r;
        }

        static Expression CreateRangeExpr(ParseNode x, ref int i)
        {
            Expression r = CreateOrExpr(x, ref i);
            if (ChildNodeMatches(x, ref i, ".."))
                r = new BinaryOperation("..", r, CreateOrExpr(x, ref i));
            return r;
        }

        static Expression CreateCondExpr(ParseNode x, ref int i)
        {
            Expression r = CreateRangeExpr(x, ref i);
            // TODO: support the ternary "a ? b : c" operator
            return r;
        }

        static Expression CreateSpecialExpr(ParseNode x, ref int i)
        {
            if (i >= x.GetNumChildren())
                throw new Exception("Internal parse error");
            ParseNode child = x.GetChild(i);
            
            if (child.Label == "funexpr")
            {
                ++i;
                FunExpr r = new FunExpr();
                r.formals = CreateFormalArgs(child.GetChild("arglist"));
                r.rettype = CreateTypeRef(child.GetChild("typedecl")); 
                r.body = CreateCodeBlock(child.GetChild("codeblock"));
                return r;
            }
            else if (child.Label == "table")
            {
                ++i;
                TableExpr r = new TableExpr();
                r.fielddefs = CreateFormalArgs(child.GetChild("arglist"));
                foreach (ParseNode row in child.GetChild("rows").Children)
                    r.AddRow(CreateCompoundExpr(row));
                return r;            
            }
            else if (child.Label == "record")
            {
                ++i;
                RecordExpr r = new RecordExpr();
                r.fielddefs = CreateFormalArgs(child.GetChild("arglist"));
                r.fields = CreateCompoundExpr(child.GetChild("recordfields"));
                return r;
            }
            else
            {
                return CreateCondExpr(x, ref i);
            }
        }

        static Expression CreateAssignmentExpr(ParseNode x, ref int i)
        {
            int old = i;
            Expression r = CreateSpecialExpr(x, ref i);
            Assure(x, r != null, "failed to create expression");
            Assure(x, i > old, "internal error, expression index not updated");
            if (i >= x.GetNumChildren())
                return r;
            string sOp = x.GetChild(i).ToString();
            switch (sOp)
            {
                case "=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, CreateSpecialExpr(x, ref i));
                case "+=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, new BinaryOperation("+", r, CreateSpecialExpr(x, ref i)));
                case "-=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, new BinaryOperation("-", r, CreateSpecialExpr(x, ref i)));
                case "*=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, new BinaryOperation("*", r, CreateSpecialExpr(x, ref i)));
                case "/=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, new BinaryOperation("/", r, CreateSpecialExpr(x, ref i)));
                case "%=":
                    if (++i >= x.GetNumChildren())
                        throw new Exception("illegal expression");
                    return new Assignment(r, new BinaryOperation("%", r, CreateSpecialExpr(x, ref i)));
                default:
                    // TODO: support other assignment operators.
                    return r;
            }
        }

        /// <summary>
        /// Creates a simple non-compound expression
        /// </summary>
        /// <param name="x"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        static Expression CreateBasicExpr(ParseNode x, ref int i)
        {
            return CreateAssignmentExpr(x, ref i);
        }

        /// <summary>
        /// This function might be called by the top-level CreateExpr(ParseNode x), 
        /// or by CreateDelimitedExprList(ParseNode x)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        static Expression CreateExpr(ParseNode x, ref int i)
        {
            Assure(x, x.GetNumChildren() > 0, "Cannot create an expression from a node with no children");

            int old = i;
            Expression r = CreateBasicExpr(x, ref i);
            Assure(x, r != null, "is not a valid expression");
            Assure(x, i > old, "internal error, current expression index was not updated");

            // Are there more expressions in the list?
            if (i < x.GetNumChildren())
            {
                // Advance past "," which is potentially an expression
                ParseNode tmp = x.GetChild(i);

                // Advance to comma 
                if (tmp.ToString() == ",")
                    i++;
            }
            return r;
        }

        static public Expression CreateExpr(string s)
        {
            ParseNode node = ParserState.Parse(HeronGrammar.CompoundExpr, s);
            if (node == null) return null;
            return CreateExpr(node);
        }

        /// <summary>
        /// This is the top-level expression creation function.
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static Expression CreateExpr(ParseNode x)
        {
            int i = 0;
            Expression r = CreateExpr(x, ref i);
            Assure(x, i == x.GetNumChildren(), "Could not parse entire expression");
            return r;
        }
        #endregion

        #region public functions    
        static public ModuleDefn ParseModule(ProgramDefn p, string s, string fileName)
        {
            ParseNode node = ParserState.Parse(HeronGrammar.Module, s);
            if (node == null)
                return null;
            ModuleDefn r = CodeModelBuilder.CreateModule(p, node, fileName);
            return r;
        }

        static public ModuleDefn ParseFile(ProgramDefn p, string sFileName)
        {
            ParseNode node;
            string sFileContents = File.ReadAllText(sFileName);
            try
            {
                node = ParserState.Parse(HeronGrammar.File, sFileContents);
            }
            catch (ParsingException e)
            {
                Console.WriteLine("Parsing exception occured in file " + sFileName);
                Console.WriteLine("at character " + e.context.col + " of line " + e.context.row);
                Console.WriteLine(e.context.msg);
                Console.WriteLine(e.context.line);
                Console.WriteLine(e.context.ptr);
                throw;
            }

            if (node == null)
            {
                Console.WriteLine("Ill-formed Heron file " + sFileName);
                throw new Exception();
            }

            try
            {
                ModuleDefn r = CodeModelBuilder.CreateModule(p, node, sFileName);
                return r;
            }
            catch (ParsingException e)
            {
                Console.WriteLine("Parsing exception occured in file " + sFileName);
                Console.WriteLine("at character " + e.context.col + " of line " + e.context.row);
                Console.WriteLine(e.context.msg);
                Console.WriteLine(e.context.line);
                Console.WriteLine(e.context.ptr);
                throw;
            }
            catch (Exception e)
            {
                Console.WriteLine("Error occured during construction of code model in file " + sFileName);
                Console.WriteLine(e.Message);
                throw;
            }
        }

        static public ClassDefn ParseClass(ModuleDefn m, string s)
        {
            ParseNode node = ParserState.Parse(HeronGrammar.Class, s);
            if (node == null)
                return null;
            ClassDefn r = CodeModelBuilder.CreateClass(m, node);
            return r;
        }

        static public InterfaceDefn ParseInterface(ModuleDefn m, string s)
        {
            ParseNode node = ParserState.Parse(HeronGrammar.Interface, s);
            if (node == null)
                return null;
            InterfaceDefn r = CodeModelBuilder.CreateInterface(m, node);
            return r;
        }
        #endregion

        #region overrides
        public override HeronType Type
        {
            get { return PrimitiveTypes.CodeModelBuilderType; }
        }
        #endregion


    }
}
